package com.adjaisd.scarecrow.service.impl;

import com.adjaisd.scarecrow.dto.DeviceInfoDTO;
import com.adjaisd.scarecrow.dto.DeviceStatusDTO;
import com.adjaisd.scarecrow.dto.QuotaDTO;
import com.adjaisd.scarecrow.dto.QuotaInfo;
import com.adjaisd.scarecrow.entity.Alarm;
import com.adjaisd.scarecrow.entity.Device;
import com.adjaisd.scarecrow.entity.DeviceStatus;
import com.adjaisd.scarecrow.entity.Quota;
import com.adjaisd.scarecrow.mapper.DeviceMapper;
import com.adjaisd.scarecrow.mapper.QuotaMapper;
import com.adjaisd.scarecrow.repo.TimeSeriesRepository;
import com.adjaisd.scarecrow.service.AlarmService;
import com.adjaisd.scarecrow.service.DeviceService;
import com.adjaisd.scarecrow.service.DeviceStatusService;
import com.adjaisd.scarecrow.service.QuotaService;
import com.adjaisd.scarecrow.vo.DeviceQuotaVO;
import com.adjaisd.scarecrow.vo.Pager;
import com.adjaisd.scarecrow.vo.QuotaAlarmVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author adjaisd
 * @since 2022-03-24
 */
@Service
public class QuotaServiceImpl extends ServiceImpl<QuotaMapper, Quota> implements QuotaService {

    @Autowired
    QuotaMapper quotaMapper;

    @Override
    public boolean existQuotaByNumberUID(Long number, long userId) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Quota::getNumber, number)
                .eq(Quota::getOwnerId, userId)
                .gt(Quota::getCreateTime, 0L);
        return this.list(wrapper).size() > 0;
    }

    @Override
    public Quota getQuotaByID(Long quotaId, Long userId) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Quota::getId, quotaId)
                .eq(Quota::getOwnerId, userId);
        List<Quota> list = this.list(wrapper);
        if (list.size() == 0) return null;
        else return list.get(0);
    }

    @Override
    public List<Quota> getQuotaListByUID(Long userId) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Quota::getOwnerId, userId)
                .gt(Quota::getCreateTime, 0L)
                .orderByAsc(Quota::getNumber);
        return this.list(wrapper);
    }

    @Override
    public List<String> getAllSubject() {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        wrapper.lambda().gt(Quota::getCreateTime, 0L);
        List<String> collect = this.list(wrapper).stream().map(Quota::getSubject).collect(Collectors.toList());
        return new ArrayList<>(Sets.newHashSet(collect));
    }

    @Override
    public Quota getQuota(Long number, String name, String subject, String valueKey, String valueType, Long userId) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Quota::getNumber, number)
                .eq(Quota::getName, name)
                .eq(Quota::getSubject, subject)
                .eq(Quota::getValueKey, valueKey)
                .eq(Quota::getValueType, valueType)
                .eq(Quota::getOwnerId, userId);
        List<Quota> quotas = this.list(wrapper);
        if (quotas.size() > 0) return quotas.get(0);
        else return null;
    }

    @Override
    public boolean existSubject(String subject) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Quota::getSubject, subject)
                .gt(Quota::getCreateTime, 0L);
        return this.list(wrapper).size() > 0;
    }

    @Override
    public boolean addQuota(Quota quota) {
        return quotaMapper.insert(quota) > 0;
    }


    @Override
    public boolean delQuota(Long number, String name, String subject, String valueKey, String valueType, Long userId) {
        Quota quota = getQuota(number, name, subject, valueKey, valueType, userId);
        if (quota == null) return false;
        quota.setCreateTime(0L);
        return quotaMapper.updateById(quota) > 0;
    }

    List<Quota> getQuotaList(Long number) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Quota::getNumber, number)
                .gt(Quota::getCreateTime, 0L);
        return this.list(wrapper);
    }

    @Autowired
    DeviceMapper deviceMapper;

    @Override
    public DeviceInfoDTO analysis(String topic, Map<String, Object> payloadMap) {
        //1.获取指标配置
        //根据主题查询指标列表
        if (!payloadMap.containsKey("number")) return null;
        System.out.println("解析普通数据payloadMap = " + payloadMap);
        Long number = Long.parseLong(payloadMap.get("number") + "");

        List<Quota> quotaList = getQuotaList(number);
        if (quotaList.size() == 0) return null;

        //2.封装设备信息
        Long deviceId = deviceMapper.selectIdByNumber(number);
        DeviceStatusDTO deviceStatusDTO = new DeviceStatusDTO();
        deviceStatusDTO.setDeviceId(deviceId);

        //3.封装指标列表  :  循环我们根据主题名称查询得指标列表，到报文中提取，如果能够提到，进行封装
        List<QuotaDTO> quotaDTOList = new LinkedList<>();
        for (Quota quota : quotaList) {
            String quotaKey = quota.getValueKey();//指标key

            if (payloadMap.containsKey(quotaKey)) {
                QuotaDTO quotaDTO = new QuotaDTO();
                //复制指标配置信息
                BeanUtils.copyProperties(quota, quotaDTO);

                //指标值封装
                //指标分为两种  1.数值  2.非数值（string boolean）
                //1.数值   value 存储数值  stringValue :存储数值字符串
                //2.非数值  value 0   stringValue:内容

                //如果是非数值
                if ("String".equals(quotaDTO.getValueType()) ||
                        "Boolean".equals(quotaDTO.getValueType())) {
                    quotaDTO.setStringValue((String) payloadMap.get(quotaKey));
                    quotaDTO.setValue(0d);
                } else {    //如果是数值
                    if (payloadMap.get(quotaKey) instanceof String) {
                        quotaDTO.setValue(Double.valueOf((String) payloadMap.get(quotaKey)));
                        quotaDTO.setStringValue((String) payloadMap.get(quotaKey));
                    } else {
                        quotaDTO.setValue(Double.valueOf(payloadMap.get(quotaKey) + ""));
                        quotaDTO.setStringValue(quotaDTO.getValue() + "");
                    }
                    quotaDTO.setDeviceId(deviceId);
                }
                quotaDTOList.add(quotaDTO);
            }
        }
        //4.封装设备+指标列表返回
        return new DeviceInfoDTO(deviceStatusDTO, quotaDTOList);
    }

    @Autowired
    TimeSeriesRepository timeSeriesRepository;

    @Override
    public void saveQuotaToInflux(List<QuotaDTO> quotaDTOList) {
        for (QuotaDTO quotaDTO : quotaDTOList) {
            QuotaInfo quotaInfo = new QuotaInfo();
            BeanUtils.copyProperties(quotaDTO, quotaInfo);
            // 处理类型不一致
            quotaInfo.setDeviceId(quotaDTO.getDeviceId() + "");
            quotaInfo.setAlarmLevel(quotaDTO.getAlarmLevel() + "");
            quotaInfo.setQuotaId(quotaDTO.getId() + "");
            timeSeriesRepository.add(quotaInfo);
        }
    }

    @Override
    public List<QuotaInfo> getLastQuotaList(Long deviceId) {
        String ql = "select last(value),* from quota where deviceId='" + deviceId + "' group by quotaId";
        return timeSeriesRepository.query(ql, QuotaInfo.class);
    }

    @Autowired
    DeviceService deviceService;

    @Override
    public Pager<QuotaAlarmVO> queryQuotaAlarmPage(long userId, long page, long pageSize, String name, String number) {
        Page<QuotaAlarmVO> deviceStatusPage = searchQuotaAlarm(userId, page, pageSize, name, number);
        return new Pager<>(deviceStatusPage);
    }

    @Override
    public Pager<DeviceQuotaVO> queryDeviceQuotaPage(long userId, long page, long pageSize, String number, String tag, int state) {
        // 根据tag与state参数找到设备状态列表
        QueryWrapper<DeviceStatus> queryWrapper = new QueryWrapper<>();
        if (!Strings.isNullOrEmpty(tag)) {   // 标签存在
            LinkedList<String> tagList = new LinkedList<>();
            if (tag.contains(",")) tagList.addAll(Arrays.stream(tag.split(",")).collect(Collectors.toList()));
            else tagList.add(tag);
            for (String s : Sets.newHashSet(tagList))
                queryWrapper.lambda().like(DeviceStatus::getTag, s);
        }
        if (state > -1) {   // 设备状态存在
            if (state == 0) //在线
                queryWrapper.lambda().eq(DeviceStatus::getStatus, true)
                        .eq(DeviceStatus::getOnline, true);
            if (state == 1) //离线(离线或关闭)
                queryWrapper.lambda().eq(DeviceStatus::getStatus, false)
                        .or().eq(DeviceStatus::getOnline, false);
        }
        List<DeviceStatus> deviceStatusList = deviceStatusService.list(queryWrapper);
        List<Long> deviceIdList = deviceStatusList.stream().map(DeviceStatus::getId).collect(Collectors.toList());


        // 根据number或用户ID分页查找设备
        QueryWrapper<Device> wrapper = new QueryWrapper<>();
        if (!Strings.isNullOrEmpty(number)) {   // 设备号存在
            wrapper.lambda().eq(Device::getNumber, Long.parseLong(number));
        } else {
            // 根据用户ID获取设备列表
            List<Device> deviceList = deviceService.getDeviceListByUID(userId);
            List<Long> numberList = deviceList.stream().map(Device::getNumber).collect(Collectors.toList());
            wrapper.lambda().in(Device::getNumber, numberList);
        }
        if (deviceIdList.size() == 0) {
            Pager<DeviceQuotaVO> pager = new Pager<>(page, pageSize);
            pager.setItems(new LinkedList<>());
            return pager;
        }
        wrapper.lambda().in(Device::getId, deviceIdList);
        Page<Device> p = new Page<>(page, pageSize);
        Page<Device> devicePage = deviceService.page(p, wrapper);

        // 封装DeviceQuotaVO对象
        List<DeviceQuotaVO> deviceQuotaVOList = new LinkedList<>();
        for (Device device : devicePage.getRecords()) {
            DeviceQuotaVO deviceQuotaVO = new DeviceQuotaVO();
            BeanUtils.copyProperties(device, deviceQuotaVO);

            // 找到设备下存在的指标
            QueryWrapper<Quota> wrapper1 = new QueryWrapper<>();
            wrapper1.lambda().gt(Quota::getCreateTime, 0L)
                    .eq(Quota::getNumber, device.getNumber());
            List<Quota> quotaList = quotaService.list(wrapper1);

            DeviceStatus statusByID = deviceStatusService.getDeviceStatusByID(device.getId());
            deviceQuotaVO.setStatus(statusByID.getStatus());
            deviceQuotaVO.setOnline(statusByID.getOnline());

            deviceQuotaVO.setQuotaList(quotaList);
            deviceQuotaVOList.add(deviceQuotaVO);
        }

        Page<DeviceQuotaVO> pageResult = new Page<>();
        BeanUtils.copyProperties(devicePage, pageResult);
        pageResult.setRecords(deviceQuotaVOList);
        return new Pager<>(pageResult);
    }

    @Override
    public IPage<Quota> queryNumberQuota(Long userId, Long page, Long pageSize) {
        Page<Quota> pageResult = new Page<>(page, pageSize);
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        String[] types = {"number", "Long", "Integer", "Double"};
        List<String> list = Arrays.stream(types).collect(Collectors.toList());
        wrapper.select("distinct name,unit");    // 去重
        wrapper.lambda().eq(Quota::getOwnerId, userId)
                .gt(Quota::getCreateTime, 0L)
                .in(Quota::getValueType, list);
        return this.page(pageResult, wrapper);
    }

    @Override
    public Boolean existQuota(Long userId, String quotaName, String unit) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        String[] types = {"number", "Long", "Integer", "Double"};
        List<String> list = Arrays.stream(types).collect(Collectors.toList());
        wrapper.lambda().eq(Quota::getOwnerId, userId)
                .eq(Quota::getName, quotaName)
                .eq(Quota::getUnit, unit)
                .in(Quota::getValueType, list)
                .gt(Quota::getCreateTime, 0L);
        List<Quota> quotaList = this.list(wrapper);
        return quotaList.size() > 0;
    }

    @Autowired
    DeviceStatusService deviceStatusService;

    @Autowired
    AlarmService alarmService;

    @Autowired
    QuotaService quotaService;

    private Page<QuotaAlarmVO> searchQuotaAlarm(Long userId, Long page, Long pageSize, String name, String number) {
        QueryWrapper<Quota> wrapper = new QueryWrapper<>();
        if (!Strings.isNullOrEmpty(number)) {   // 设备号存在
            wrapper.lambda().eq(Quota::getNumber, Long.parseLong(number));
        } else {
            // 根据用户ID获取设备列表
            List<Device> deviceListByUID = deviceService.getDeviceListByUID(userId);
            List<Long> numberList = deviceListByUID.stream().map(Device::getNumber).collect(Collectors.toList());
            wrapper.lambda().in(Quota::getNumber, numberList);
        }
        if (!Strings.isNullOrEmpty(name)) {   // 标签存在
            wrapper.lambda().like(Quota::getName, name);
        }

        System.out.println("sql = " + wrapper.getSqlSelect());

        Page<Quota> pageResult = new Page<>(page, pageSize);
        Page<Quota> quotaPage = this.page(pageResult, wrapper);

        // 找到对应状态的指标及报警信息封装成QuotaAlarmVO对象
        List<QuotaAlarmVO> list = new LinkedList<>();
        for (Quota quota : quotaPage.getRecords()) {
            QuotaAlarmVO quotaAlarmVO = new QuotaAlarmVO();
            BeanUtils.copyProperties(quota, quotaAlarmVO);
            Device device = deviceService.getDeviceByNumber(quota.getNumber());
            DeviceStatus status = deviceStatusService.getDeviceStatusByID(device.getId());
            quotaAlarmVO.setDeviceId(device.getId() + "");
            quotaAlarmVO.setStatus(status.getStatus());
            quotaAlarmVO.setOnline(status.getOnline());

            List<Alarm> alarmList = alarmService.getAlarmListByQId(quota.getId());
            quotaAlarmVO.setAlarmList(alarmList);
            list.add(quotaAlarmVO);
        }

        Page<QuotaAlarmVO> res = new Page<>();
        BeanUtils.copyProperties(quotaPage, res);
        res.setRecords(list);
        return res;
    }

}

/*
influx 查询语句
> select * from quota where time>'2022-04-03' and time<='2022-04-05' and alarmName=~/温度/ and deviceId=~/^3/ and level!='0' order by desc limit 10 OFFSET 0;
name: quota
time                alarm alarmName deviceId name quotaId reference stringValue unit value
----                ----- --------- -------- ---- ------- --------- ----------- ---- -----
1648999162487000000 1     温度过低      3        温度   4       [-5,35]   -4.0        ℃    -4
 */

/*
time                alarm alarmLevel alarmName deviceId name quotaId reference stringValue unit value
----                ----- ---------- --------- -------- ---- ------- --------- ----------- ---- -----
1649456010439000000 1                          123456   温度   1       0-10                  摄氏度  12
1649577010283000000 0     0          正常        1        湿度   2       [0,100]   9.0         %    9
1649577010475000000 0     0          正常        1        湿度   2       [0,100]   78.0        %    78
1649577010540000000 0     0          正常        1        湿度   2       [0,100]   15.0        %    15
1649577010582000000 1     3          温度过高      2        温度   1       [-5,40]   62.0        ℃    62
1649577010619000000 0     0          正常        2        湿度   3       [0,100]   78.0        %    78
1649577010660000000 0     0          正常        2        湿度   3       [0,100]   59.0        %    59
1649577010697000000 0     0          正常        2        温度   1       [-5,40]   15.0        ℃    15

 */